package com.ylr.redis.framework.service;

import com.ylr.base.common.enums.ResultEnum;
import com.ylr.base.common.exception.YlrException;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2023-07-27 15:39:37
 * className: RedissonService
 * version: 1.0
 * description:
 */
@Service("redissonService")
//@ConditionalOnProperty(name = "redisson.enabled", havingValue = "true")
public class RedissonService {

    private static final Logger log = LoggerFactory.getLogger(RedissonService.class);

    private final RedissonClient redissonClient;

    public RedissonService(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    /**
     * 加锁运行
     * 没有返回值
     * @param key      锁键值
     * @param runnable 目标代码逻辑
     */
    public void lockRun(String key, Runnable runnable) {
        // 获取锁对象
        RLock lock = redissonClient.getLock(key);
        // 加锁
        lock.lock();
        log.info("加锁成功，准备执行目标逻辑代码。key={}", key);
        try {
            // 执行目标代码
            runnable.run();
            log.info("目标逻辑代码执行完成");
        } finally {
            // 解锁
            this.unLock(lock);
        }
    }

    /**
     * 加锁运行
     * 没有返回值
     * @param key      锁键值
     * @param lockTime 加锁时长
     * @param unit     时间单位
     * @param runnable 目标代码逻辑
     */
    public void lockRun(String key, long lockTime, TimeUnit unit, Runnable runnable) {
        // 获取锁对象
        RLock lock = redissonClient.getLock(key);
        // 加锁
        lock.lock(lockTime, unit);
        log.info("加锁成功，准备执行目标逻辑代码。key={} lockTime={} unit={}", key, lockTime, unit);
        try {
            // 执行目标代码
            runnable.run();
            log.info("目标逻辑代码执行完成");
        } finally {
            // 解锁
            this.unLock(lock);
        }
    }

    /**
     * 加锁运行
     * 返回目标代码执行结果
     * @param key      锁键值
     * @param supplier 目标代码逻辑
     */
    public <T> T lockRun(String key, Supplier<T> supplier) {
        // 获取锁对象
        RLock lock = redissonClient.getLock(key);
        // 加锁
        lock.lock();
        log.info("加锁成功，准备执行目标逻辑代码。key={}", key);
        try {
            // 执行目标代码并返回结果
            return supplier.get();
        } finally {
            // 解锁
            this.unLock(lock);
        }
    }

    /**
     * 加锁运行
     * 返回目标代码执行结果
     * @param key      锁键值
     * @param lockTime 加锁时长
     * @param unit     时间单位
     * @param supplier 目标代码逻辑
     */
    public <T> T lockRun(String key, long lockTime, TimeUnit unit, Supplier<T> supplier) {
        // 获取锁对象
        RLock lock = redissonClient.getLock(key);
        // 加锁
        lock.lock(lockTime, unit);
        log.info("加锁成功，准备执行目标逻辑代码。key={} lockTime={} unit={}", key, lockTime, unit);
        try {
            // 执行目标代码并返回结果
            return supplier.get();
        } finally {
            // 解锁
            this.unLock(lock);
        }
    }

    /**
     * 尝试加锁运行，如果加锁失败，不执行
     * 没有返回值
     * @param key      锁键值
     * @param runnable 目标代码逻辑
     */
    public void tryLockRun(String key, Runnable runnable) {
        // 获取锁对象
        RLock lock = redissonClient.getLock(key);
        // 加锁
        boolean bool = lock.tryLock();
        log.info("尝试加锁完成。key={} bool={}", key, bool);
        if (!bool) {
            return;
        }
        try {
            // 执行目标代码
            runnable.run();
            log.info("目标逻辑代码执行完成");
        } finally {
            // 解锁
            this.unLock(lock);
        }
    }

    /**
     * 尝试加锁运行，如果加锁失败，不执行
     * 没有返回值
     * @param key      锁键值
     * @param waitTime 等待时长（尝试加锁失败，等待时长范围会不断重试，超时不执行）
     * @param unit     时间单位
     * @param runnable 目标代码逻辑
     */
    public void tryLockRun(String key, long waitTime, TimeUnit unit, Runnable runnable) {
        // 获取锁对象
        RLock lock = redissonClient.getLock(key);
        // 加锁
        boolean bool = false;
        try {
            bool = lock.tryLock(waitTime, unit);
        } catch (InterruptedException e) {
            log.error("尝试加锁中断，加锁失败。key={} waitTime={} unit={} message={}", key, waitTime, unit, e.getMessage(), e);
        }
        log.info("尝试加锁完成。key={} waitTime={} unit={} bool={}", key, waitTime, unit, bool);
        if (!bool) {
            return;
        }
        try {
            // 执行目标代码
            runnable.run();
            log.info("目标逻辑代码执行完成");
        } finally {
            // 解锁
            this.unLock(lock);
        }
    }

    /**
     * 尝试加锁运行，如果加锁失败，不执行
     * 没有返回值
     * @param key      锁键值
     * @param waitTime 等待时长（尝试加锁失败，等待时长范围会不断重试，超时不执行）
     * @param lockTime 加锁时长
     * @param unit     时间单位
     * @param runnable 目标代码逻辑
     */
    public void tryLockRun(String key, long waitTime, long lockTime, TimeUnit unit, Runnable runnable) {
        // 获取锁对象
        RLock lock = redissonClient.getLock(key);
        // 加锁
        boolean bool = false;
        try {
            bool = lock.tryLock(waitTime, lockTime, unit);
        } catch (InterruptedException e) {
            log.error("尝试加锁中断，加锁失败。key={} waitTime={} lockTime={} unit={} message={}", key, waitTime, lockTime,
                    unit, e.getMessage(), e);
        }
        log.info("尝试加锁完成。key={} waitTime={} lockTime={} unit={} bool={}", key, waitTime, lockTime, unit, bool);
        if (!bool) {
            return;
        }
        try {
            // 执行目标代码
            runnable.run();
            log.info("目标逻辑代码执行完成");
        } finally {
            // 解锁
            this.unLock(lock);
        }
    }

    /**
     * 尝试加锁运行，如果加锁失败，抛出异常
     * 没有返回值
     * @param key      锁键值
     * @param runnable 目标代码逻辑
     */
    public void tryLockRun(String key, Runnable runnable, YlrException ylrException) {
        // 获取锁对象
        RLock lock = redissonClient.getLock(key);
        // 加锁
        boolean bool = lock.tryLock();
        log.info("尝试加锁完成。key={} bool={}", key, bool);
        if (!bool) {
            throw ylrException;
        }
        try {
            // 执行目标代码
            runnable.run();
            log.info("目标逻辑代码执行完成");
        } finally {
            // 解锁
            this.unLock(lock);
        }
    }

    /**
     * 尝试加锁运行，如果加锁失败，抛出异常
     * 没有返回值
     * @param key      锁键值
     * @param runnable 目标代码逻辑
     */
    public void tryLockRun(String key, Runnable runnable, ResultEnum resultEnum) {
        // 获取锁对象
        RLock lock = redissonClient.getLock(key);
        // 加锁
        boolean bool = lock.tryLock();
        log.info("尝试加锁完成。key={} bool={}", key, bool);
        if (!bool) {
            throw new YlrException(resultEnum);
        }
        try {
            // 执行目标代码
            runnable.run();
            log.info("目标逻辑代码执行完成");
        } finally {
            // 解锁
            this.unLock(lock);
        }
    }

    /**
     * 尝试加锁运行，如果加锁失败，抛出异常
     * 没有返回值
     * @param key      锁键值
     * @param waitTime 等待时长（尝试加锁失败，等待时长范围会不断重试，超时抛出异常）
     * @param unit     时间单位
     * @param runnable 目标代码逻辑
     */
    public void tryLockRun(String key, long waitTime, TimeUnit unit, Runnable runnable, YlrException ylrException) {
        // 获取锁对象
        RLock lock = redissonClient.getLock(key);
        // 加锁
        boolean bool;
        try {
            bool = lock.tryLock(waitTime, unit);
        } catch (InterruptedException e) {
            log.error("尝试加锁中断，加锁失败。key={} waitTime={} unit={} message={}", key, waitTime, unit, e.getMessage(), e);
            throw ylrException;
        }
        log.info("尝试加锁完成。key={} waitTime={} unit={} bool={}", key, waitTime, unit, bool);
        if (!bool) {
            throw ylrException;
        }
        try {
            // 执行目标代码
            runnable.run();
            log.info("目标逻辑代码执行完成");
        } finally {
            // 解锁
            this.unLock(lock);
        }
    }

    /**
     * 尝试加锁运行，如果加锁失败，抛出异常
     * 没有返回值
     * @param key      锁键值
     * @param waitTime 等待时长（尝试加锁失败，等待时长范围会不断重试，超时抛出异常）
     * @param lockTime 加锁时长
     * @param unit     时间单位
     * @param runnable 目标代码逻辑
     */
    public void tryLockRun(String key, long waitTime, long lockTime, TimeUnit unit, Runnable runnable,
                           YlrException ylrException) {
        // 获取锁对象
        RLock lock = redissonClient.getLock(key);
        // 加锁
        boolean bool;
        try {
            bool = lock.tryLock(waitTime, lockTime, unit);
        } catch (InterruptedException e) {
            log.error("尝试加锁中断，加锁失败。key={} waitTime={} lockTime={} unit={} message={}", key, waitTime, lockTime,
                    unit, e.getMessage(), e);
            throw ylrException;
        }
        log.info("尝试加锁完成。key={} waitTime={} lockTime={} unit={} bool={}", key, waitTime, lockTime, unit, bool);
        if (!bool) {
            throw ylrException;
        }
        try {
            // 执行目标代码
            runnable.run();
            log.info("目标逻辑代码执行完成");
        } finally {
            // 解锁
            this.unLock(lock);
        }
    }

    /**
     * 尝试加锁运行，如果加锁失败，不执行
     * 没有返回值
     * @param key          锁键值
     * @param supplier     目标代码逻辑
     * @param defaultValue 默认值，如果目标逻辑不执行，则返回该值
     */
    public <T> T tryLockRun(String key, Supplier<T> supplier, T defaultValue) {
        // 获取锁对象
        RLock lock = redissonClient.getLock(key);
        // 加锁
        boolean bool = lock.tryLock();
        log.info("尝试加锁完成。key={} bool={}", key, bool);
        if (!bool) {
            return defaultValue;
        }
        try {
            // 执行目标代码
            return supplier.get();
        } finally {
            // 解锁
            this.unLock(lock);
        }
    }

    /**
     * 尝试加锁运行，如果加锁失败，不执行
     * 没有返回值
     * @param key          锁键值
     * @param waitTime     等待时长（尝试加锁失败，等待时长范围会不断重试，超时返回默认值）
     * @param unit         时间单位
     * @param supplier     目标代码逻辑
     * @param defaultValue 默认值，如果目标逻辑不执行，则返回该值
     */
    public <T> T tryLockRun(String key, long waitTime, TimeUnit unit, Supplier<T> supplier, T defaultValue) {
        // 获取锁对象
        RLock lock = redissonClient.getLock(key);
        // 加锁
        boolean bool;
        try {
            // 尝试加锁
            bool = lock.tryLock(waitTime, unit);
        } catch (InterruptedException e) {
            log.error("尝试加锁中断，加锁失败。key={} waitTime={} unit={} message={}", key, waitTime, unit, e.getMessage(), e);
            return defaultValue;
        }
        log.info("尝试加锁完成。key={} waitTime={} unit={} bool={}", key, waitTime, unit, bool);
        if (!bool) {
            return defaultValue;
        }
        try {
            // 执行目标代码
            return supplier.get();
        } finally {
            // 解锁
            this.unLock(lock);
        }
    }

    /**
     * 尝试加锁运行，如果加锁失败，不执行
     * 没有返回值
     * @param key          锁键值
     * @param waitTime     等待时长（尝试加锁失败，等待时长范围会不断重试，超时返回默认值）
     * @param lockTime     加锁时长
     * @param unit         时间单位
     * @param supplier     目标代码逻辑
     * @param defaultValue 默认值，如果目标逻辑不执行，则返回该值
     */
    public <T> T tryLockRun(String key, long waitTime, long lockTime, TimeUnit unit, Supplier<T> supplier, T defaultValue) {
        // 获取锁对象
        RLock lock = redissonClient.getLock(key);
        // 加锁
        boolean bool;
        try {
            // 尝试加锁
            bool = lock.tryLock(waitTime, lockTime, unit);
        } catch (InterruptedException e) {
            log.info("尝试加锁中断，加锁失败。key={} waitTime={} lockTime={} unit={} message={}", key, waitTime, lockTime,
                    unit, e.getMessage(), e);
            return defaultValue;
        }
        log.info("尝试加锁完成。key={} waitTime={} lockTime={} unit={} bool={}", key, waitTime, lockTime, unit, bool);
        if (!bool) {
            return defaultValue;
        }
        try {
            // 执行目标代码
            return supplier.get();
        } finally {
            // 解锁
            this.unLock(lock);
        }
    }
    

    /**
     * 尝试加锁运行，如果加锁失败，抛出异常
     * 没有返回值
     * @param key      锁键值
     * @param supplier 目标代码逻辑
     */
    public <T> T tryLockRun(String key, Supplier<T> supplier, YlrException ylrException) {
        // 获取锁对象
        RLock lock = redissonClient.getLock(key);
        // 加锁
        boolean bool = lock.tryLock();
        log.info("尝试加锁完成。key={} bool={}", key, bool);
        if (!bool) {
            throw ylrException;
        }
        try {
            // 执行目标代码
            return supplier.get();
        } finally {
            // 解锁
            this.unLock(lock);
        }
    }

    /**
     * 尝试加锁运行，如果加锁失败，抛出异常
     * 没有返回值
     * @param key      锁键值
     * @param waitTime 等待时长（尝试加锁失败，等待时长范围会不断重试，超时抛出异常）
     * @param unit     时间单位
     * @param supplier 目标代码逻辑
     */
    public <T> T tryLockRun(String key, long waitTime, TimeUnit unit, Supplier<T> supplier, YlrException ylrException) {
        // 获取锁对象
        RLock lock = redissonClient.getLock(key);
        // 加锁
        boolean bool;
        try {
            bool = lock.tryLock(waitTime, unit);
        } catch (InterruptedException e) {
            log.error("尝试加锁中断，加锁失败。key={} waitTime={} unit={} message={}", key, waitTime, unit, e.getMessage(), e);
            throw ylrException;
        }
        log.info("尝试加锁完成。key={} waitTime={} unit={} bool={}", key, waitTime, unit, bool);
        if (!bool) {
            throw ylrException;
        }
        try {
            // 执行目标代码
            return supplier.get();
        } finally {
            // 解锁
            this.unLock(lock);
        }
    }

    /**
     * 尝试加锁运行，如果加锁失败，抛出异常
     * 没有返回值
     * @param key      锁键值
     * @param waitTime 等待时长（尝试加锁失败，等待时长范围会不断重试，超时抛出异常）
     * @param lockTime 加锁时长
     * @param unit     时间单位
     * @param supplier 目标代码逻辑
     */
    public <T> T tryLockRun(String key, long waitTime, long lockTime, TimeUnit unit, Supplier<T> supplier,
                            YlrException ylrException) {
        // 获取锁对象
        RLock lock = redissonClient.getLock(key);
        // 加锁
        boolean bool;
        try {
            bool = lock.tryLock(waitTime, lockTime, unit);
        } catch (InterruptedException e) {
            log.error("尝试加锁中断，加锁失败。key={} waitTime={} lockTime={} unit={} message={}", key, waitTime, unit, lockTime,
                    e.getMessage(), e);
            throw ylrException;
        }
        log.info("尝试加锁完成。key={} waitTime={} lockTime={} unit={} bool={}", key, waitTime, lockTime, unit, bool);
        if (!bool) {
            throw ylrException;
        }
        try {
            // 执行目标代码
            return supplier.get();
        } finally {
            // 解锁
            this.unLock(lock);
        }
    }

    /**
     * 解锁操作
     * @param lock 锁对象
     */
    private void unLock(RLock lock) {
        if (lock.isLocked() && lock.isHeldByCurrentThread()) {
            // 解锁
            lock.unlock();
            log.info("解锁成功");
        }
    }
}
